#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;

#include "Color.h"

////////////////////////////////////////////////////////////////////////////////
LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
	ls(nomFich), ts() {
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::analyse() {
	arbre=programme();
	cout << "Syntaxe correcte." << endl;
}

//////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::traduire(){
  Color color;
	 vector<SymboleValue *> table= (ts).getTable();
	 for (unsigned int i=0; i<table.size(); i++){
		 if( *((Symbole*)(table[i]))=="<VARIABLE>" ){
			 if (typeid(((table[i])->getValeur()))==typeid(int)){
				 cout << color.textcolor(BRIGHT, GREEN, BLACK) << "int" << color.textcolor(BRIGHT, WHITE, BLACK) << " " << (table[i])->getChaine() << ";"<<endl ;
			 }
			 else if (typeid(((table[i])->getValeur()))==typeid(string)){
				 cout << color.textcolor(BRIGHT, GREEN, BLACK) << "string" << color.textcolor(BRIGHT, WHITE, BLACK) << " " << (table[i])->getChaine() << ";"<<endl ;
			 }
		 }
	 }
	 getArbre ()->traduire();
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::programme() {
	// <programme> ::= debut <seq_inst> fin FIN_FICHIER

	sauterSymCour("debut");
	Noeud* si = seqInst();
	sauterSymCour("fin");
	testerSymCour("<FINDEFICHIER>");
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::seqInst() {
	// <seqInst> ::= <inst> ; { <inst> ; }

	NoeudSeqInst* si = new NoeudSeqInst();
	do {
		si->ajouteInstruction(inst());
		sauterSymCour(";");
	} while (ls.getSymCour()=="<VARIABLE>" ||
                 ls.getSymCour()=="si" ||
                 ls.getSymCour()=="selon" ||
                 ls.getSymCour()=="tantque" ||
                 ls.getSymCour()=="repeter" ||
                 ls.getSymCour()=="pour" ||
                 ls.getSymCour()=="lire" ||
                 ls.getSymCour()=="ecrire");
	// tant que le symbole courant est un debut possible d'instruction...
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::inst() {
// <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <instPour> | <instLire> | <instEcrire>
    if (ls.getSymCour()=="<VARIABLE>")
	return affectation();
    else if (ls.getSymCour()=="si")
        return instSi();
    else if (ls.getSymCour()=="selon")
        return instSelon();
    else if (ls.getSymCour()=="tantque")
        return instTq();
    else if (ls.getSymCour()=="repeter")
        return instRepeter();
    else if (ls.getSymCour()=="pour")
        return instPour();
    else if (ls.getSymCour()=="lire")
        return instLire();
    else if (ls.getSymCour()=="ecrire")
        return instEcrire();
    else {
	erreur("<instr>");
        return NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSi() {
// <instSi> ::= si ( <expBool> ) <seqInst>
//                 { sinonsi ( <expBool> ) <seqInst> }
//                 [ sinon <seqInst> ] finsi
    sauterSymCour("si");
    sauterSymCour("(");
    Noeud* expB = expBool();
    sauterSymCour(")");
    Noeud* si = seqInst();
    NoeudInstSi* instSi = new NoeudInstSi(expB,si);

    while (ls.getSymCour()=="sinonsi") {
        sauterSymCour("sinonsi");
        sauterSymCour("(");
        instSi->ajouterCasExp(expBool());
        sauterSymCour(")");
        instSi->ajouterCasTabInst(seqInst());
    }
    if (ls.getSymCour()=="sinon") {
        sauterSymCour("sinon");
        instSi->ajouterCasExp(NULL);
        instSi->ajouterCasTabInst(seqInst());
    }
    sauterSymCour("finsi");
    return instSi;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSelon() {
// <instSelon> ::= selon ( <variable> )
//                   cas  <facteur> : <seqInst>
//                 { cas  <facteur> : <seqInst> }
//                 [ defaut : <seqInst> ] finselon
    sauterSymCour("selon");
    sauterSymCour("(");
    Noeud* variable = ts.chercheAjoute(ls.getSymCour());
    sauterSymCour("<VARIABLE>");
    sauterSymCour(")");

    sauterSymCour("cas");
    Noeud* exp = facteur();
    sauterSymCour(":");
    Noeud* si = seqInst();
    NoeudInstSelon* instSelon = new NoeudInstSelon(exp,si,variable);

    while (ls.getSymCour()=="cas")
    {
        sauterSymCour("cas");
        instSelon->ajouterCasExp(facteur());
        sauterSymCour(":");
        instSelon->ajouterCasTabInst(seqInst());
    }

    if (ls.getSymCour()=="defaut") {
        sauterSymCour("defaut");
        sauterSymCour(":");
        instSelon->ajouterCasExp(NULL);
        instSelon->ajouterCasTabInst(seqInst());
    }
    sauterSymCour("finselon");

    return instSelon;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instLire() {
// <instLire> ::= lire ( <variable> )
    sauterSymCour("lire");
    sauterSymCour("(");
    Noeud* variable = ts.chercheAjoute(ls.getSymCour());
    sauterSymCour("<VARIABLE>");
    sauterSymCour(")");
    return new NoeudInstLire(variable);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instEcrire() {
// <instEcrire> ::= ecrire ( <expression> | <chaine> )
    sauterSymCour("ecrire");
    sauterSymCour("(");
    Noeud* exp=NULL;		//initialisation
    if (ls.getSymCour()=="<CHAINE>") {
	exp = ts.chercheAjoute(ls.getSymCour());
        sauterSymCour("<CHAINE>");
    }
    else
        exp = expression();
    sauterSymCour(")");
    return new NoeudInstEcrire(exp);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instTq() {
// <instTq> ::= tantque ( <expBool> ) <seqInst> fintantque
    sauterSymCour("tantque");
    sauterSymCour("(");
    Noeud* expB = expBool();
    sauterSymCour(")");
    Noeud* si =seqInst();
    sauterSymCour("fintantque");
    return new NoeudInstTq(expB,si);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instRepeter() {
// <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )
    sauterSymCour("repeter");
    Noeud* si = seqInst();
    sauterSymCour("jusqua");
    sauterSymCour("(");
    Noeud* expB = expBool();
    sauterSymCour(")");
    return new NoeudInstRepeter(expB,si);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instPour() {
// <instPour> ::= pour ( <affectation> ; <expBool> ; <affectation> ) <seqInst> finpour
    sauterSymCour("pour");
    sauterSymCour("(");
    Noeud* affect = affectation();
    sauterSymCour(";");
    Noeud* expB = expBool();
    sauterSymCour(";");
    Noeud* affectInc = affectation();
    sauterSymCour(")");
    Noeud* si = seqInst();
    sauterSymCour("finpour");
    return new NoeudInstPour(expB, si, affect, affectInc);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::affectation() {
// <affectation> ::= <variable> = <expression>

	testerSymCour("<VARIABLE>");
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
	ls.suivant();
	sauterSymCour("=");
        Noeud* exp=NULL;		//initialisation
        if (ls.getSymCour()=="<CHAINE>") {
		testerSymCour("<CHAINE>");
		exp = ts.chercheAjoute(ls.getSymCour());
		ls.suivant();
	}
        else
            exp = expression();
	return new NoeudAffectation(var,exp);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expression() {
// <expression> ::= <terme> { <opAdd> <terme> }

	Noeud* term = terme();
	while (ls.getSymCour()=="+" || ls.getSymCour()=="-" ) {
		Symbole operateur = opAdd(); // on stocke le symbole de l'opérateur
		Noeud* termDroit=terme(); // lecture de l'operande droit
		term = new NoeudOperateurBinaire(operateur,term,termDroit); // const. du noeud
	}
	return term;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::terme() {
// <terme> ::= <facteur> { <opMult> <facteur> }

	Noeud* fact = facteur();
	while (ls.getSymCour()=="*" || ls.getSymCour()=="/") {
		Symbole operateur = opMult(); // on stocke le symbole de l'opérateur
		Noeud* factDroit=facteur(); // lecture de l'operande droit
		fact = new NoeudOperateurBinaire(operateur,fact,factDroit); // const. du noeud
	}
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::facteur() {
// <facteur> ::= <entier> | <variable> | <opUnaire> <expBool> | ( <expBool> )

	Noeud* fact=NULL;

	if (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="<ENTIER>") {
		fact=ts.chercheAjoute(ls.getSymCour());
		ls.suivant();
	} else if ((ls.getSymCour()=="-") || (ls.getSymCour()=="non")) {
		Symbole operateur = opUnaire(); // on stocke le symbole de l'opérateur
		fact = new NoeudOperateurBinaire(operateur, ts.chercheAjoute(Symbole("0")), expBool());
	} else if (ls.getSymCour()=="(") {
		ls.suivant();
		fact=expBool();
		sauterSymCour(")");
	} else
		erreur("<facteur>");
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opAdd() {
// <opAdd> ::= + | -
	Symbole operateur;
	if (ls.getSymCour()=="+" || ls.getSymCour()=="-" ) {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opAdd>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opMult() {
// <opMult> ::= * | /
	Symbole operateur;
	if (ls.getSymCour()=="*" || ls.getSymCour()=="/" ) {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opMult>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expBool() {
// <expBool> ::= <termeBool> { <opOu> <termeBool> }

	Noeud* term = termeBool();
	while (ls.getSymCour()=="ou") {
		Symbole operateur = opOu(); // on stocke le symbole de l'opérateur
		Noeud* termDroit=termeBool(); // lecture de l'operande droit
                term = new NoeudOperateurBinaire(operateur,term,termDroit); // const. du noeud
	}
        return term;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::termeBool() {
// <termeBool> ::= <relation> { <opEt> <relation> }

	Noeud* rel = relation();
	while (ls.getSymCour()=="et") {
		Symbole operateur = opEt(); // on stocke le symbole de l'opérateur
		Noeud* relDroit=relation(); // lecture de l'operande droit
                rel = new NoeudOperateurBinaire(operateur,rel,relDroit); // const. du noeud
	}
        return rel;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opEt() {
// <opEt> ::= et
	Symbole operateur;
	if (ls.getSymCour()=="et") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opEt>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opOu() {
// <opOu> ::= ou
	Symbole operateur;
	if (ls.getSymCour()=="ou") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opOu>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::relation() {
// <relation> ::= <expression> { <opRel> <expression> }

	Noeud* expr = expression();
	while (ls.getSymCour()=="==" || ls.getSymCour()=="!=" ||
                ls.getSymCour()=="<" || ls.getSymCour()=="<=" ||
                ls.getSymCour()==">" || ls.getSymCour()==">=" ) {
		Symbole operateur = opRel(); // on stocke le symbole de l'opérateur
		Noeud* exprDroit=expression(); // lecture de l'operande droit
		expr = new NoeudOperateurBinaire(operateur,expr,exprDroit); // const. du noeud
	}
    return expr;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opRel() {
// <opRel> ::= == | != | < | <= | > | >=
	Symbole operateur;
	if (ls.getSymCour()=="==" || ls.getSymCour()=="!=" ||
		ls.getSymCour()=="<" || ls.getSymCour()=="<=" ||
		ls.getSymCour()==">" || ls.getSymCour()==">=") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opRel>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opUnaire() {
// <opUnaire> ::= - | non
	Symbole operateur;
	if (ls.getSymCour()=="-" || ls.getSymCour()=="non" ) {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		erreur("<opUnaire>");
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::testerSymCour(string ch) {
	if (ls.getSymCour() != ch) {
		cout << endl << "-------- Erreur ligne " << ls.getLigne()
				<< " - Colonne " << ls.getColonne() << endl << "   Attendu : "
				<< ch << endl << "   Trouve  : " << ls.getSymCour() << endl
				<< endl;
		throw ErreurSyntaxe();
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
	testerSymCour(ch);
	ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreur(string mess) {
	cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
			<< ls.getColonne() << endl << "   Attendu : " << mess << endl
			<< "   Trouve  : " << ls.getSymCour() << endl << endl;
	throw ErreurSyntaxe();
}
